#include "monitor.h"
#include "expr.h"
#include "watchpoint.h"
#include "temu.h"
#include "../../include/memory/memory.h"

#include <stdlib.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <OS_gui.h>

void cpu_exec(uint32_t);
void display_reg();

/* We use the `readline' library to provide more flexibility to read from stdin. */
char* rl_gets() {
	static char *line_read = NULL;

	if (line_read) {
		free(line_read);
		line_read = NULL;
	}

	// line_read = readline("(temu) ");
	
	if (line_read && *line_read) {
		add_history(line_read);
	}

	return line_read;
}

static int cmd_c(char *args) {
	cpu_exec(-1);
	return 0;
}

static int cmd_q(char *args) {
	return -1;
}

static int cmd_help(char *args);
static int cmd_p(char *args);
static int cmd_w(char* args);
static int cmd_d(char* args);
static int cmd_si(char* args);
static int cmd_info(char* args);
static int cmd_x(char* args);

static struct {
	char *name;
	char *description;
	int (*handler) (char *);
} cmd_table [] = {
	{ "help", "Display informations about all supported commands", cmd_help },
	{ "c", "Continue the execution of the program", cmd_c },
	{ "q", "Exit TEMU", cmd_q },
	{ "p", "Calculate the value of an expression", cmd_p},
	{ "si","single steps",cmd_si},
	{ "info","print registers",cmd_info},
	// { "x","print memory",cmd_x},
	{ "w", "Add a watch point", cmd_w},
	{ "d", "Delete a watch point", cmd_d},
	{ "x", "Scan memory", cmd_x}

	/* TODO: Add more commands */

};

#define NR_CMD (sizeof(cmd_table) / sizeof(cmd_table[0]))

static int cmd_help(char *args) {
	/* extract the first argument */
	char *arg = strtok(NULL, " ");
	int i;

	if(arg == NULL) {
		/* no argument given */
		for(i = 0; i < NR_CMD; i ++) {
			GUI_printf(BLUE_TEXT, "%s - %s\n", cmd_table[i].name, cmd_table[i].description);
		}
	}
	else {
		for(i = 0; i < NR_CMD; i ++) {
			if(strcmp(arg, cmd_table[i].name) == 0) {
				GUI_printf(BLUE_TEXT, "%s - %s\n", cmd_table[i].name, cmd_table[i].description);
				return 0;
			}
		}
		GUI_printf(RED_TEXT, "Unknown command '%s'\n", arg);
	}
	return 0;
}

static int cmd_p(char* args){
	bool success = false;
	int res = expr(args, &success);
	if(success == 1) GUI_printf(WHITE_TEXT, "%d (0x%x)\n", res, res);
	else GUI_printf(RED_TEXT, "Expression calculate failed.");
	return 0;
}

static int cmd_w(char* args){
	wpAdd(args);
	return 0;
}

// static int cmd_x(char* args){
// 	if(args==NULL){
// 		printf("error!\n");
// 		return 0;
// 	}
// 	int N;
// 	bool success;
// 	char exprs[35] = "";
// 	sscanf(args,"%d %s",&N,exprs);
// 	int res = expr(exprs,&success);
// 	int i;
// 	for(i = 0;i < N;i++){
		
// 		if(i%4==0)
// 		printf("0x%08x:",res + i*4);
// 		printf(" 0x%08x",swaddr_read(res + i * 4, 4, R_DS));
// 		if(i%4==3)
// 		printf("\n");
// 	}
// 	if(i%4!=0)printf("\n");
// 	return 0;
// }

static int cmd_d(char* args){
	wpDel(args);
	return 0;
}

static int cmd_si(char *args){
	bool success;
	if(args == NULL)cpu_exec(1);
	else cpu_exec(expr(args, &success));
	return 0;
}

static int cmd_info(char *args){
	if(args == NULL) {
		GUI_printf(BLUE_TEXT, "usage: info [w/r]");
		return 0;
	}
	if(args[0] == 'r'){
		display_reg();
	}
	else if(args[0] == 'w'){
		wpDisplay();
	}
	// else assert(0);	
	return 0;
}

static int cmd_x(char* args){
	int i;
	int *N = malloc(sizeof(int));
	bool success;
	uint32_t addr;
	char *num = strtok(args, " ");
	*N = atoi(num);
	args = strtok(NULL, "\n");
	addr = (int32_t)(int16_t)expr(args, &success);
	char *mem_str = malloc(64);
	memset(mem_str, 0, sizeof(*mem_str));
	GUI_printf(BLUE_TEXT, "Scan %d memory from %s", *N, args);
	if(success == false) return 0;
	for(i = 1; i <= *N; ++i){
		if(i % 4 == 1) GUI_printf(YELLOW_TEXT, "0x%08x:\n", addr);
		sprintf(mem_str, "%s%08x  ", mem_str, mem_read(addr, 4));
		addr += 4;
		if(i % 4 == 0) {
			add_Text(mem_str, GREEN_TEXT);
			memset(mem_str, 0, sizeof(*mem_str));
		}
	}
	if(mem_str != NULL) add_Text(mem_str, GREEN_TEXT);
	if(*N / 4 != 0 || *N < 4) GUI_printf(WHITE_TEXT, "\n");
	return 0;
}

int ui_mainloop() {
	while(1) {
		
		draw_All();
		int cmd_status = GUI_helper();
		if(cmd_status < 0) return cmd_status;

		// char *str = rl_gets();
		// char *str_end = str + strlen(str);

		/* extract the first token as the command */
		// char *cmd = strtok(str, " ");
		// if(cmd == NULL) { continue; }

		/* treat the remaining string as the arguments,
		 * which may need further parsing
		 */
		// char *args = cmd + strlen(cmd) + 1;
		// if(args >= str_end) {
		// 	args = NULL;
		// }

		// int i;
		// for(i = 0; i < NR_CMD; i ++) {
		// 	if(strcmp(cmd, cmd_table[i].name) == 0) {
		// 		if(cmd_table[i].handler(args) < 0) { return; }
		// 		break;
		// 	}
		// }

		// if(i == NR_CMD) { GUI_printf("Unknown command '%s'\n", cmd); }
		// if(i == NR_CMD) { 
		// 	GUI_printf("Unknown command '%s'\n", cmd);
		// }
	}
	return -1;
}

int GUI_uio(){
	char *str = uio_handle();
	char *str_end = str + strlen(str);
	if(str == str_end) return 0;
	char *cmd = strtok(str, " ");

	char *args = cmd + strlen(cmd) + 1;
	if(args >= str_end) {
		args = NULL;
	}

	int i;
	for(i = 0; i < NR_CMD; i ++) {
		if(strcmp(cmd, cmd_table[i].name) == 0) {
			if(cmd_table[i].handler(args) < 0) { return GUI_q(); }
			break;
		}
	}

	if(i == NR_CMD) { GUI_printf(RED_TEXT, "Unknown command '%s'\n", cmd); }
	return 0;
}
